﻿using ConsoleApp2.Layer;
using ConsoleApp2.util;
using DenseCRF;
using FCN;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Model
{

         
        
  
    public class ConvGRUCell
    {

        //public void Backward(Matrix[] data, Matrix[] ytag)
        //{
        //    MSELoss loss = new MSELoss();
        //    Matrix[] temp= loss.MESBackwrad(tempall, data, ytag, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);

        //}

        int input_size;
        int hidden_size;
        int kernel_size;
        public convlayer ConvGates;
        public int _stride = 1;
        public int _padding = 0;
        public int input_sizeNUM { get; set; }
        public int output_sizenum { get; set; }
        public ConvGRUCell(int _input_size, int _hidden_size, int _kernel_size)
        {
            input_size = _input_size;
            hidden_size = _hidden_size;
            kernel_size = _kernel_size;
            input_sizeNUM = _input_size + _hidden_size;
            output_sizenum = 4 * _hidden_size;
            _padding = (kernel_size - 1) / 2;
            ConvGates = new convlayer(_stride, _padding, kernel_size, input_sizeNUM, output_sizenum);


        }
        SigmodLayer slingate;
        SigmodLayer slinoutgate, slinremember_gate;
        MulLayer mulingcell = new MulLayer();
        MulLayer mulremember_gate = new MulLayer();
        MulLayer hiddenmul = new MulLayer();
        TanhLayer tanhcell = new TanhLayer();
        TanhLayer tanhcell_gate = new TanhLayer();
        Matrix[,] tcell;
        
        public Matrix[,] forward(Matrix[,] input, Matrix[,] prev_state)
        {
            var batch_size = input.GetLength(0);
            if (prev_state == null)
            {
                prev_state = new Matrix[batch_size, hidden_size];
                for (var a = 0; a < batch_size; a++)
                    for (var b = 0; b < hidden_size; b++)
                        prev_state[a, b] = new Matrix(input[0, 0].values.GetLength(0), input[0, 0].values.GetLength(1));
                // +list(input.data.size()[2:])
            }

            var prev_hidden = Matrix.Clone(prev_state);
            var prev_cell = Matrix.Clone(prev_state);
            // torch.cat((input, hidden), 1)
            //  c1 = self.ConvGates()
            //(rt, ut) = c1.chunk(2, 1) 
            inputallconv = Matrix.cat(input, prev_hidden, 1);
            tempallconv = ConvGates.Forward(inputallconv);
            Matrix[][,] TRg = Matrix.chunk(tempallconv, 4, 1);
            var in_gate = TRg[0];
            var remember_gate = TRg[1];
           var out_gate = TRg[2];
            var cell_gate = TRg[3];
            ////    in_gate = f.sigmoid(in_gate)
            ////remember_gate = f.sigmoid(remember_gate)
            ////out_gate = f.sigmoid(out_gate)
            slingate = new SigmodLayer();
            in_gate = slingate.forward(in_gate);
            slinremember_gate = new SigmodLayer();
            remember_gate = slinremember_gate.forward(remember_gate);
            slinoutgate = new SigmodLayer();
             out_gate = slinoutgate.forward(out_gate);
            ////# apply tanh non linearity
            ////    cell_gate = f.tanh(cell_gate)
            cell_gate = tanhcell_gate.forward(cell_gate);
            var RPT = mulremember_gate.forward(remember_gate, prev_cell);
          
            var ingcell = mulingcell.forward(in_gate, cell_gate);
         
            var cell = Matrix.MatrixAdd(RPT, ingcell);
            ////cell = (remember_gate * prev_cell) + (in_gate * cell_gate)
            ////hidden = out_gate * f.tanh(cell)
            //  tanhcell.forward()
             tcell=  tanhcell.forward(cell);
              var hidden = hiddenmul.forward(out_gate, tcell);
            //    return new Matrix[2][,] { cell, hidden };
            //return hidden;

            return hidden;
           // return tempallconv;
        }
        Matrix[,] tempallconv, inputallconv;
        public Matrix[,] Backward(Matrix[,] data, Matrix[,] ytag)
        {
         
            MSELoss loss = new MSELoss();
            Matrix[,] Dgirdsum = null; 
          
             
             
              
                Matrix[][,] inttemp = Matrix.chunk(inputallconv, ConvGates.inChannels, 1);
               
              
            for (var ini = 0; ini < inttemp.Length; ini++)
            {
                


                Matrix[,] Dgird = null;
                D4dbackDelegate Dback = delegate (int indexx,int indexy, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);
                    bb = slingate.backward(indexx,indexy,x,y, bb);
                    bb = mulingcell.backward(indexx, indexy, x, y, bb);
                    bb = tanhcell.backward(indexx, indexy, x, y, bb);
                    bb = hiddenmul.backwardY(indexx, indexy, x, y, bb);
                     
                    return bb;
                };
                Matrix[,] MEStemp= loss.MESBackwradconv(inttemp[ini], data, ytag, ConvGates.Kszie, ConvGates.stride, ConvGates.padding, Dback);
                Dgird = MEStemp;
                //---------------------------------------------


                D4dbackDelegate Dback2 = delegate (int indexx, int indexy, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);
                    
                    bb = slinremember_gate.backward(indexx, indexy, x, y, bb);
                    bb = mulremember_gate.backward(indexx, indexy, x, y, bb);
                    bb = tanhcell.backward(indexx, indexy, x, y, bb);
                    bb = hiddenmul.backwardY(indexx, indexy, x, y, bb);
                    return bb;
                };
                Matrix[,] temp = loss.MESBackwradconv(inttemp[ini], data, ytag, ConvGates.Kszie, ConvGates.stride, ConvGates.padding, Dback2);
               
              //  temp = loss.MESDconv(temp, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = Matrix.cat(Dgird, temp, 1);

                //---------------------------------------------
                D4dbackDelegate Dback3 = delegate (int indexx, int indexy, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);


                    bb = slinoutgate.backward(indexx, indexy, x, y, bb);
                    bb = hiddenmul.backward(indexx, indexy, x, y, bb);
                    return bb;
                };
                temp = loss.MESBackwradconv(inttemp[ini], data, ytag, ConvGates.Kszie, ConvGates.stride, ConvGates.padding, Dback3);
              
              //  temp = loss.MESDconv(temp, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = Matrix.cat(Dgird, temp, 1);
                //---------------------------------------------

                D4dbackDelegate Dback4 = delegate (int indexx, int indexy, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);

                    bb = tanhcell_gate.backward(indexx, indexy, x, y, bb);
                    bb = mulingcell.backwardY(indexx, indexy, x, y, bb);
                    bb = tanhcell.backward(indexx, indexy, x, y, bb);
                    bb = hiddenmul.backwardY(indexx, indexy, x, y, bb);

                    
                    return bb;
                };
                temp = loss.MESBackwradconv(inttemp[ini], data, ytag, ConvGates.Kszie, ConvGates.stride, ConvGates.padding, Dback4);
               
               // temp = loss.MESDconv(temp, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = Matrix.cat(Dgird, temp, 1);
                //---------------------------------------------
                if (Dgirdsum == null)
                    Dgirdsum = Dgird;
                else
                    Dgirdsum = Matrix.cat(Dgirdsum, Dgird, 0);

            }



            //cell_gateconv
            //  Dgird = Matrix.cat(Dgird, temloss, 1);

            return Dgirdsum;
            //  //temp = hiddenmul.backward(temp);
             

        }


        public void update(Matrix[,] DGRID,float learning_rate)
        {

            for (var i = 0; i < ConvGates.outChannels; i++)
            {
                float outsumbais = 0f;
                for (var j = 0; j < ConvGates.inChannels; j++)
                {
                    ConvGates.weights[j, i]=Matrix.MatrixSub(ConvGates.weights[j,i], Matrix.multiply(DGRID[j, i], learning_rate));
                   outsumbais += Matrix.sum(DGRID[j, i]);
                    
                }
                ConvGates.basicData[i] = ConvGates.basicData[i]-(outsumbais* learning_rate);
            }
        }

    }

}
